package com.ruoyi.common.wx;

import com.alibaba.fastjson2.JSONObject;
import com.github.binarywang.wxpay.bean.order.WxPayNativeOrderResult;
import com.github.binarywang.wxpay.bean.request.WxPayRefundRequest;
import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderRequest;
import com.github.binarywang.wxpay.bean.result.WxPayRefundResult;
import com.github.binarywang.wxpay.bean.result.WxPayUnifiedOrderResult;
import com.github.binarywang.wxpay.constant.WxPayConstants;
import com.github.binarywang.wxpay.exception.WxPayException;
import com.github.binarywang.wxpay.service.WxPayService;
import com.github.binarywang.wxpay.util.SignUtils;
import com.github.wxpay.sdk.WXPayUtil;
import lombok.SneakyThrows;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.*;

/**
 * @description: 支付相关通用服务
 */
@Service
public class PayService {


    private static final Logger log = LoggerFactory.getLogger(PayService.class);


    @Autowired
    private WxPayService wxPayService;

    /**
     * 根据传入的tradeType来进行不同类型的支付
     * @param dto 商品参数
     * @param tradeType WxPayConstants.TradeType.MWEB为H5支付/WxPayConstants.TradeType.NATIVE为扫码支付/WxPayConstants.TradeType.JSAPI为JSAPI支付
     * @return
     */
    public Object transactions(HttpServletRequest request,WxPayDTO dto,String tradeType) {
        try {
            if (tradeType != null && !tradeType.isEmpty()){
                if (WxPayConstants.TradeType.MWEB.equals(tradeType)){
                    //H5支付
                    return noMiniappPay(dto, tradeType);
                }else if (WxPayConstants.TradeType.NATIVE.equals(tradeType)){
                    //NATIVE支付
                    return noMiniappPay(dto, tradeType);
                }else if (WxPayConstants.TradeType.JSAPI.equals(tradeType)){
                    //JSAPI支付
                    return miniappPay(request,dto);
                }
            }
        } catch (Exception e) {
            // 处理异常
            e.printStackTrace();
            return null;
        }
        return null;
    }

    private String noMiniappPay(WxPayDTO dto, String tradeType) throws WxPayException {
        //设置请求参数
        WxPayUnifiedOrderRequest request = new WxPayUnifiedOrderRequest();
        request.setOutTradeNo(dto.getOutTradeNo());
        request.setTotalFee(dto.getTotalFee());
        request.setBody(dto.getBody());
        HttpServletRequest httpServletRequest = getHttpServletRequest();
        request.setSpbillCreateIp(httpServletRequest.getRemoteAddr());
        request.setNotifyUrl(dto.getNotifyUrl());
        request.setProductId(dto.getOutTradeNo());
        //设置下单方式
        request.setTradeType(tradeType);
        // 调用统一下单接口
        WxPayNativeOrderResult result = wxPayService.createOrder(request);
        // Native支付返回的二维码/H5支付返回的跳转链接
        String codeUrl = result.getCodeUrl();
        // 返回codeUrl给前端
        return codeUrl;
    }

    /**
     * @param request
     * @param dto
     * @return
     */
    @SneakyThrows
    public Map<String, String> miniappPay(HttpServletRequest request, WxPayDTO dto) {
        try {
            log.info("微信小程序支付,请求参数:{}", dto);

            // 创建 WxPayUnifiedOrderRequest 对象
            WxPayUnifiedOrderRequest WXRequest = new WxPayUnifiedOrderRequest();
            WXRequest.setAppid(wxPayService.getConfig().getAppId());
            WXRequest.setMchId(wxPayService.getConfig().getMchId());
            WXRequest.setNonceStr(WXPayUtil.generateNonceStr());
            WXRequest.setBody(dto.getBody());
            WXRequest.setOutTradeNo(dto.getOutTradeNo());
            WXRequest.setTotalFee(dto.getTotalFee());
            // 获取客户端 IP（示例，需根据实际框架调整）
            String clientIp = request.getHeader("X-Forwarded-For");
            WXRequest.setSpbillCreateIp(clientIp != null ? clientIp : request.getRemoteAddr());
            WXRequest.setNotifyUrl(dto.getNotifyUrl());
            WXRequest.setTradeType(WxPayConstants.TradeType.JSAPI.toString());
            WXRequest.setOpenid(dto.getOpenId());
            WXRequest.setTimeStart(dto.getTimeStart());
            WXRequest.setTimeExpire(dto.getTimeExpire());
            WXRequest.setSignType(WxPayConstants.SignType.MD5.toString());

            // 构建签名所需的参数 Map
            Map<String, String> signParams = new TreeMap<>();
            signParams.put("appid", WXRequest.getAppid());
            signParams.put("mch_id", WXRequest.getMchId());
            signParams.put("nonce_str", WXRequest.getNonceStr());
            signParams.put("body", WXRequest.getBody());
            signParams.put("out_trade_no", WXRequest.getOutTradeNo());
            signParams.put("total_fee", String.valueOf(WXRequest.getTotalFee()));
            signParams.put("spbill_create_ip", WXRequest.getSpbillCreateIp());
            signParams.put("notify_url", WXRequest.getNotifyUrl());
            signParams.put("trade_type", WXRequest.getTradeType());
            signParams.put("openid", dto.getOpenId());
            signParams.put("time_start", WXRequest.getTimeStart());
            signParams.put("time_expire", WXRequest.getTimeExpire());
            signParams.put("sign_type", WXRequest.getSignType());

            // 生成统一下单签名（使用官方工具类）
            String sign = WXPayUtil.generateSignature(signParams, wxPayService.getConfig().getMchKey());
            WXRequest.setSign(sign);

            // 发起统一下单（获取 WxPayUnifiedOrderResult）
            WxPayUnifiedOrderResult orderResult = wxPayService.unifiedOrder(WXRequest);

            // 构建 Map<String, String>
            Map<String, String> resultMap = new java.util.HashMap<>();
            resultMap.put("appid", orderResult.getAppid());
            resultMap.put("mch_id", orderResult.getMchId());
            resultMap.put("nonce_str", orderResult.getNonceStr());
            resultMap.put("sign", orderResult.getSign());
            resultMap.put("result_code", orderResult.getResultCode());
            resultMap.put("prepay_id", orderResult.getPrepayId()); // 提取 prepay_id

            log.info("统一下单响应:{}", resultMap);


            // 提取prepay_id
            String prepayId = resultMap.get("prepay_id");
            if (prepayId == null) {
                log.error("prepay_id为空");
                return null;
            }

            // 生成前端JSAPI支付参数（与Demo一致）
            return getJsapiPayParams(prepayId);
        } catch (Exception e) {
            log.error("微信小程序支付统一下单失败", e);
            return null;
        }
    }

    private Map<String, String> getJsapiPayParams(String prepayId) throws Exception {
        Map<String, String> jsapiParams = new TreeMap<>();
        jsapiParams.put("appId", wxPayService.getConfig().getAppId());
        jsapiParams.put("timeStamp", String.valueOf(System.currentTimeMillis() / 1000));
        jsapiParams.put("nonceStr", WXPayUtil.generateNonceStr());
        jsapiParams.put("package", "prepay_id=" + prepayId); // 注意参数名是"package"
        jsapiParams.put("signType", WxPayConstants.SignType.MD5.toString());

        // 生成前端签名（使用官方工具类）
        String sign = WXPayUtil.generateSignature(jsapiParams,
                wxPayService.getConfig().getMchKey());
        jsapiParams.put("paySign", sign);

        return jsapiParams;
    }
    /**
     * 微信退款
     * @param body
     * @return
     */
    public void wxRefund(WxRefundDTO body) {
        WxPayRefundRequest refundRequest = new WxPayRefundRequest();
        refundRequest.setOutTradeNo(body.getOutTradeNo());
        refundRequest.setOutRefundNo(body.getOutRefundNo());
        refundRequest.setTotalFee(body.getTotalFee());
        refundRequest.setRefundFee(body.getRefundFee());
        refundRequest.setRefundDesc("商品退款");
        refundRequest.setNotifyUrl(body.getNotifyUrl());

        try {
            WxPayRefundResult result = wxPayService.refund(refundRequest);
            System.out.println("微信退款成功,返回参数{}" + JSONObject.toJSONString(result));
        } catch (WxPayException e) {
            System.out.println("微信退款异常,返回参数{}" + JSONObject.toJSONString(e));
        }
    }


    /**
     * 获取请求对象
     *
     * @return
     */
    private HttpServletRequest getHttpServletRequest() {
        HttpServletRequest request = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
        Enumeration<String> headerNames = request.getHeaderNames();
        while (headerNames.hasMoreElements()) {
            String key = headerNames.nextElement();
            System.out.println("请求头：" + key + "值：" + request.getHeader(key));
        }
        return request;
    }

    /**
     * 获取到JS支付相关配置
     *
     * @param weiXinPayOrder
     * @return
     */
    private Map<String, String> getWxJsapiPayParam(WxPayUnifiedOrderResult weiXinPayOrder) {
        WxPayWebDTO wxPayParam = new WxPayWebDTO();
        String package_ = "prepay_id=" + weiXinPayOrder.getPrepayId();
        wxPayParam.setAppId(weiXinPayOrder.getAppid());
        wxPayParam.setTimeStamp(System.currentTimeMillis() / 1000 + "");
        wxPayParam.setNonceStr(UUID.randomUUID().toString().replace("-", ""));
        wxPayParam.setPackage1(package_);
        wxPayParam.setSignType(WxPayConstants.SignType.MD5);
        log.info("微信小程序支付,wxPayParam返回参数:{}", JSONObject.toJSONString(wxPayParam));

        //WxPayParam中属性package1，签名使用key是package
        Map<String, String> signParam = new LinkedHashMap<String, String>();
        signParam.put("appId", weiXinPayOrder.getAppid());
        signParam.put("nonceStr", wxPayParam.getNonceStr());
        signParam.put("package", package_);
        signParam.put("signType", WxPayConstants.SignType.MD5);
        signParam.put("timeStamp", wxPayParam.getTimeStamp());
        String paySign = SignUtils.createSign(signParam,WxPayConstants.SignType.MD5, this.wxPayService.getConfig().getMchKey(), new String[0]);
        log.info("微信小程序支付,paySign返回参数:{}", JSONObject.toJSONString(paySign));
        log.info("微信小程序支付,signParam返回参数:{}", JSONObject.toJSONString(signParam));

        signParam.put("paySign", paySign);
        return signParam;
    }
}

